#include "md_sysmsg.h"
#include <errno.h>

/*msg 队列消息 发送固定结构*/
struct	msgbuf{
	long mtype;
    char acmess[];          
};

/** 
 * @brief 创建一个消息队列，需要指定路径，适合与其它进程配合传递消息的场景   
 * @param int ique_id             指定消息队列id             
 * 该队列特性固定为：IPC_CREAT | IPC_EXCL  | IPC_NOWAIT | 0777   ：不存在就创建，无阻塞，所有权限
 * @return 成功返回队列 ID，失败返回 -1, 并设置 erron
 */
int que_create(const char *pcpathname, int iid)
{
    int ierro = -1;
    key_t tkey;
    if(iid > 255)
    {
        printf("id > 255 fail.");
        return ierro;
    }
    if(NULL == pcpathname){
        tkey = ftok("./",iid );
    }
    else{
        tkey = ftok(pcpathname,iid );
    }
    ierro = msgget(tkey,IPC_CREAT | IPC_NOWAIT | 0777);
	msgctl(ierro, IPC_RMID , NULL);
	ierro = msgget(tkey,IPC_CREAT | IPC_NOWAIT | 0777);

    if(ierro < 0 )
    {
        printf("md_private_vqueue_create fail. errno :%d",errno);
    }
    return ierro;
}

/*
    删除消息队列
*/
int que_remove(int ifd)
{
    int ierro   = -1;
    if(ifd < 0)
        return ierro;
    ierro = msgctl(ifd, IPC_RMID , NULL);
    return ierro;    
}


static int get_ctl_info(int ifd,struct msqid_ds *ptmsg_info)
{
    int ierro = -1;
    if(ifd < 0)
        return -1;
    ierro = msgctl(ifd, IPC_STAT, ptmsg_info);
    printf("this queue info: ");
    printf("msg_info.msg_ctime:    %ld ",ptmsg_info->msg_ctime);
    printf("msg_info.msg_lrpid:    %d ",ptmsg_info->msg_lrpid);
    printf("msg_info.msg_lspid:    %d ",ptmsg_info->msg_lspid);
    printf("msg_info.msg_qbytes:   %ld ",ptmsg_info->msg_qbytes);
    printf("msg_info.msg_cbytes:   %ld ",ptmsg_info->__msg_cbytes);
    printf("msg_info.msg_qnum:     %ld ",ptmsg_info->msg_qnum);
    printf("msg_info.msg_rtime:    %ld ",ptmsg_info->msg_rtime);
    printf("msg_info.msg_stime:    %ld ",ptmsg_info->msg_stime);
    return ierro;
}

/**
 * @brief 无阻塞发送队列数据
 * @param itype 用于指定所请求的消息类型
 * @param             
 * 
 * @return 成功返回数据长度  失败-1
 */
static int que_write(int ifd,long itype,uint8_t *pu8mess,uint16_t u16messlen)
{
    int ierro = -1;
    if(ifd < 0)
        return -1;
    if(itype <= 0)
    {
        printf("msgbuff itype <= 0 fail.");
        return -1;
    }
    uint8_t *pu8data = malloc(u16messlen + sizeof(struct msgbuf));
    struct msgbuf* ptmsgbuff    = (struct msgbuf*)pu8data;
    ptmsgbuff->mtype    = itype;
    memcpy(ptmsgbuff->acmess,pu8mess,u16messlen);
    ierro = msgsnd(ifd,ptmsgbuff,u16messlen,IPC_NOWAIT);
    if(ierro < 0)
        printf("msgsend fail. errno :%d",errno);

    if(pu8data != NULL)
        free(pu8data);

    return ierro;
}

/**
 * @brief 无阻塞接收队列数据
 * @param itype 用于指定所请求的消息类型
 * @param             
 * 
 * @return 成功返回接受数据的长度  失败< 0
 */
static int que_read(int ifd,int is_block,long *itype,uint8_t *pu8mess,uint16_t u16messlen)
{
    int ierro = -1;
    if(ifd < 0)
        return -1;
    uint8_t *pu8data = malloc(u16messlen + sizeof(struct msgbuf));
    struct msgbuf *ptmsgbuff    = (struct msgbuf*)pu8data;
	if(is_block){
		ierro = msgrcv(ifd,ptmsgbuff,u16messlen,*itype, MSG_NOERROR);//阻塞
	}else{
		ierro = msgrcv(ifd,ptmsgbuff,u16messlen,*itype,IPC_NOWAIT | MSG_NOERROR);//非阻塞
	}
	*itype = ptmsgbuff->mtype;
    memcpy(pu8mess,(uint8_t *)ptmsgbuff->acmess,u16messlen);
    if(ierro < 0)
    {
        if(ENOMSG == errno) 
        {
            ierro = 0;
        }
        else{
            printf("msgrcv fail. %d",errno);
        }
    }
    if(pu8data != NULL)
        free(pu8data);
    return ierro;
}

/**
 * @brief 生成队列数据的对象
 *---
 * @param [in] pu8mess              : @n 队列消息
 * @param [in]  u32mess_len         : @n 队列消息长度
 * @param [out] ppvque_data_object  : @n 队生成队列数据的对象列消息 或 已存在的object
 * @param [out] pu32object_data_len : @n 队生成队列数据的对象列消息 或 已存在的object len
 * @return 0 - 成功; 其他: 错误.
 * @see None.
 * @note None.
 */

static int generate_que_object(uint8_t *pu8mess, 
                                        uint16_t u16mess_len, 
                                        int isrc_addr, 
                                        int idest_addr, 
                                        uint16_t u16cmd, 
                                        void **ppvque_data_object,
                                        uint16_t *pu16object_data_len)
{
    uint16_t        u16tx_que_len = 0; 
    pt_vqueue_data  ptque_data    = NULL;
    
    u16tx_que_len        = u16mess_len + sizeof(t_vqueue_data);
    *pu16object_data_len = u16tx_que_len;
    if(!*ppvque_data_object)
    {
        *ppvque_data_object  = malloc(u16tx_que_len);
         memset(*ppvque_data_object , 0 , u16tx_que_len);
    }
    
    if(!*ppvque_data_object)
    {
        printf("pvque_data_object malloc fail");
        return -1;
    }
    ptque_data = (pt_vqueue_data)(*ppvque_data_object);
    ptque_data->isrc_addr   = isrc_addr;
    ptque_data->idest_addr  = idest_addr; 
    ptque_data->u16cmd      = u16cmd;
    ptque_data->u16mess_len = u16mess_len;
    if(pu8mess)
        memcpy(ptque_data->au8mess, pu8mess, u16mess_len);
    
    return 0;
}

/**
 * 针对直接发送指针地址的应用场景单独封装的接口
 * @brief 生成队列数据的对象
 *---
 * @param [in] pu8mess              : @n 需要发送的指针
 * @param [out] ppvque_data_object  : @n 队生成队列数据的对象列消息 或 已存在的object
 * @param [out] pu32object_data_len : @n 队生成队列数据的对象列消息 或 已存在的object len
 * @return 0 - 成功; 其他: 错误.
 * @see None.
 * @note None.
 */

static int generate_que_object2(void *pu8mess, 
                                        void **ppvque_data_object,
                                        uint16_t *pu16object_data_len)
{
    *pu16object_data_len = sizeof(void *);
    if(!*ppvque_data_object)
    {
        *ppvque_data_object  = malloc(*pu16object_data_len);
         memset(*ppvque_data_object , 0 , *pu16object_data_len);
    }
    
    if(!*ppvque_data_object)
    {
        printf("pvque_data_object malloc fail");
        return -1;
    }
	if(*pu16object_data_len == 8){
		uint64_t *addr = (uint64_t *)*ppvque_data_object;
		*addr = (uint64_t)pu8mess;
	}
	else if(*pu16object_data_len == 4){
		uint32_t *addr = (uint32_t *)*ppvque_data_object;
		*addr = (uint32_t)pu8mess;
	}
    return 0;
}


/**
 * 适用于进程间传递消息
 * @brief 发送队列数据
 * @param u16cmd                    队列消息cmd控制符
 * @param pttx_que_data_mess        发送的数据
 * @param u16tx_que_mess_len        发送数据的长度
 * @param isrc_que_index            源地址
 * @param idest_que_index           目的地址
 * @return 成功返回发送数据的长度  失败< 0
 */
int tx_sys_que(int ifd,
                    uint16_t u16cmd, 
                    void* pttx_que_data_mess, 
                    uint16_t u16tx_que_mess_len, 
                    int isrc_que_index,
                    int idest_que_index)
{
    int ierro = -1;
    void *ptque_data = NULL;
    uint16_t u16data_len;
    ierro = generate_que_object((uint8_t *)pttx_que_data_mess,
                                u16tx_que_mess_len,
                                isrc_que_index,
                                idest_que_index,
                                u16cmd,
                                (void **)&ptque_data,
                                &u16data_len);
    if(!ptque_data)
    {
        printf("generate_que_object fail.");
        return -1;
    }
    ierro = que_write(ifd,idest_que_index,(uint8_t*)ptque_data,u16data_len);
    free(ptque_data);
    ptque_data = NULL;
    return ierro;
}

/**
 * 针对直接发送指针地址的应用场景单独封装的接口
 * @brief 发送队列数据
 * @param pttx_que_data_mess        发送的数据
 * @return 成功返回发送数据的长度  失败< 0
 */
int tx_sys_que2(int ifd,
					long int itype,
                    void* pttx_que_data_mess)
{
    int ierro = -1;
    void *ptque_data = NULL;
    uint16_t u16data_len;
    ierro = generate_que_object2(pttx_que_data_mess,
                                (void **)&ptque_data,
                                &u16data_len);
    if(!ptque_data)
    {
        printf("generate_que_object fail.");
        return -1;
    }
    ierro = que_write(ifd,itype,(uint8_t*)ptque_data,u16data_len);
    free(ptque_data);
    ptque_data = NULL;
    return ierro;
}


/**
 * 适用于进程间传递消息
 * @brief 接收队列数据
 * @param ptrx_que_data_mess        接受缓存
 * @param u16rx_que_mess_len        接收缓存长度（注意要比发送方长度长，否则数据将被丢弃）
 * @param idest_que_index           目的地址（数据发给谁，谁来处理）
 * @return 成功返回接收数据的长度  失败< 0
 */
int rx_sys_que(int ifd,
					int is_block,
                    long *idest_que_index,
                    uint8_t* ptrx_que_data_mess, 
                    uint16_t u16rx_que_mess_len)
{
    int ierro = -1;
    ierro = que_read(ifd,is_block,idest_que_index,ptrx_que_data_mess,u16rx_que_mess_len);
    return ierro;
}


/**
 * 适用于进程内传递消息，！！！接收处理完需要释放掉内存！！！
 * 发送的是消息指针地址，故占用消息队列空间较小；64位编译器8个字节，32位编译器4个字节
 * @brief 发送队列数据
 * @param u16cmd                    队列消息cmd控制符
 * @param pttx_que_data_mess        发送的数据
 * @param u16tx_que_mess_len        发送数据的长度
 * @param isrc_que_index            源地址
 * @param idest_que_index           目的地址
 * @return 成功返回发送数据的长度  失败< 0
 */
int tx_paddr_que(int ifd,
                     uint16_t u16cmd, 
                    void* pttx_que_data_mess, 
                    uint16_t u16tx_que_mess_len, 
                    int isrc_que_index,
                    int idest_que_index)
{
    int ierro = -1;
    void *ptque_data = NULL;
    uint16_t u16data_len;
    ierro = generate_que_object((uint8_t *)pttx_que_data_mess,
                                u16tx_que_mess_len,
                                isrc_que_index,
                                idest_que_index,
                                u16cmd,
                                (void **)&ptque_data,
                                &u16data_len);
    if(!ptque_data)
    {
        printf("generate_que_object fail.");
        return -1;
    }
    ierro = que_write(ifd,idest_que_index,(uint8_t*)&ptque_data,sizeof(void*));
    return ierro;
}

/**
 * 适用于进程内传递消息，！！！接收处理完需要释放掉内存！！！
 * 发送的是消息指针地址，故占用消息队列空间较小；64位编译器8个字节，32位编译器4个字节
 * @brief 接收队列数据
 * @param ptrx_que_addr             接受缓存,接收的是地址，要注意给8字节还是4字节
 * @param idest_que_index           目的地址（数据发给谁，谁来处理）
 * @return 成功返回接收数据的长度  失败< 0
 */
int rx_paddr_que(int ifd,int is_block, long *idest_que_index,
                    void* ptrx_que_addr)
{
    int ierro = -1;
    ierro = que_read(ifd, is_block, idest_que_index, (uint8_t*)ptrx_que_addr, sizeof(void*));
    return ierro;
}
/*地址队列消息 接收用例*/
/*
int take_paddr_que(void)
{
    uint64_t u64rxdata;  //64位编译器选择使用//
    pt_vqueue_data ptinfo;
    myqueue.rx_paddr_que(dest_addr,(uint8_t*)&u64rxdata);
    ptinfo = (pt_vqueue_data)u64rxdata;
    *
    *处理接收数据
    *
    delete []ptinfo;
    ptinfo = NULL;
}
*/
